dash: Workflow panel and Spend punchcard - #783
Conversation
Workflow panel sits beside Model efficiency and surfaces correction rate (with count), median time to first edit, pricing coverage (rendered only when a real number, never as 100% for unknown), and the top reworked files (basenames with session and edit counts). The whole panel hides when the payload carries no workflow block, so an older peer renders the dashboard unchanged. Spend punchcard is an hour-of-day by weekday matrix computed client-side from history.timeline, dot size and intensity encoding spend, with a per-cell tooltip. Cells with no bucket render empty, the panel hides when the timeline is absent, and daily-bucket periods show an honest notice instead of faking hour-of-day resolution.
ozymandiashh
left a comment
There was a problem hiding this comment.
Reviewed both components against the payload contract and, since #888 is open, against the dark token block it introduces. The token discipline here is exactly right: everything reads from var(--color-primary) / var(--color-interactive-secondary) and the semantic classes (text-tertiary-foreground, border-border, bg-popover), so both panels restyle correctly under #888's .dark overrides with zero changes. The honesty gates are also the right calls: absent workflow block hides the panel, null coverage never renders as 100%, and daily buckets refuse to fake a midnight column.
Four findings, first one is a real rendering bug:
1. The tooltip clips inside the scroll container
overflow-x-auto on the punchcard wrapper computes overflow-y to auto as well (CSS spec: once one axis is non-visible, the other cannot stay visible). The tooltip is absolutely positioned inside that container with -translate-y-full at top: hover.y - 8, so for the top row (Mon) it extends above the container's content box: it gets clipped and, because the axis is auto, can flash a vertical scrollbar that shifts the grid. Hovering Mon cells on a narrow window shows it. Fix options: track the hover position relative to the outer non-scrolling div and render the tooltip there, or give the scroll area enough top padding to contain the tooltip.
2. The hour-resolution gate admits future sub-daily-but-coarser buckets
hourResolved = timeline.bucketMinutes < 1440 is honest today because buckets are either <=60min or daily. But any future coarser-than-hour bucket (say 240min) passes the gate and piles its whole spend onto the bucket-start hour, which is precisely the misattribution the gate exists to prevent. bucketMinutes <= 60 is honest by construction and costs nothing now. Same for bucketNote: the >= 60 ? 'Hourly buckets' ternary would label a 1440 bucket "Hourly buckets" if the gate ever moved.
3. "local time" is the viewer's, not necessarily the device's
For a remote peer, points were bucketed on the peer's local hour boundaries and are re-interpreted here through the viewer's getHours(). With a whole-hour offset the punchcard is merely shifted (defensible); with a half-hour offset (IST) every remote bucket straddles two viewer cells and lands on the earlier one. Suggest labeling it "viewer local time" for remote devices, or carrying the peer tz in the payload later. Low priority, but the current label overpromises in exactly the remote case DeviceView supports.
4. Two nits
- Moving from a covered cell onto an uncovered one keeps the previous tooltip alive (uncovered cells never call
setHover), so the tooltip lingers while pointing at a cell the cursor left.setHover(null)on uncovered cells closes it. ring-black/5(punchcard dot and tooltip) is the one non-token color in the diff; on #888's dark surfaces a black ring vanishes.ring-borderorring-foreground/10keeps the edge visible in both themes.
Phase 1 and 2 of the surfacing plan for the web dashboard.
Workflow panel beside Model efficiency: correction rate with count, median time-to-first-edit, pricing coverage (renders only when computable, null is absent and never shown as 100%), and the top reworked files (basenames, edits x sessions). The whole panel gates on hasWorkflowContent, so payloads from older CLIs render today's dashboard unchanged and Model efficiency keeps full width.
Spend punchcard below it: hour-of-day by weekday matrix computed client-side from history.timeline, dot size and intensity encoding spend, per-cell exact-USD tooltips. Honesty rules: empty cells stay empty, the panel hides when the timeline is absent, and coarser-than-hourly periods (month and up produce daily buckets) show a notice pointing at Today or 7 days instead of faking a midnight column.
Verified on a real cache copy: month workflow block corrections 6 / TTFE 6m24s / coverage 95.5% with 8 reworked files; week punchcard covers all 168 cells, peak Fri 02:00 at 96.80 USD. Typecheck and vite build clean; the dash has no test harness, so verification is typecheck, build, and a rendered-DOM smoke pass.
Part of the phased surfacing plan; not for release until the wave is reviewed together.